﻿/************************************************************************

   Extended WPF Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. The commercial version of Extended WPF Toolkit also includes
   priority technical support, commercial updates, and many additional 
   useful WPF controls if you license Xceed Business Suite for WPF.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System.Collections;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Twi.COMMON.WPF.CtlExtended.Core
{
  /// <summary>
  /// An adorner that can display one and only one UIElement.  
  /// That element can be a panel, which contains multiple other elements.
  /// The element is added to the adorner's visual and logical trees, enabling it to 
  /// particpate in dependency property value inheritance, amongst other things.
  /// </summary>
  internal class UIElementAdorner<TElement> : Adorner where TElement : UIElement
  {
    #region Fields

    TElement _child = null;
    double _offsetLeft = 0;
    double _offsetTop = 0;

    #endregion // Fields

    #region Constructor

    /// <summary>
    /// Constructor. 
    /// </summary>
    /// <param name="adornedElement">The element to which the adorner will be bound.</param>
    public UIElementAdorner( UIElement adornedElement )
      : base( adornedElement )
    {
    }

    #endregion // Constructor

    #region Public Interface

    #region Child

    /// <summary>
    /// Gets/sets the child element hosted in the adorner.
    /// </summary>
    public TElement Child
    {
      get
      {
        return _child;
      }
      set
      {
        if( value == _child )
          return;

        if( _child != null )
        {
          base.RemoveLogicalChild( _child );
          base.RemoveVisualChild( _child );
        }

        _child = value;

        if( _child != null )
        {
          base.AddLogicalChild( _child );
          base.AddVisualChild( _child );
        }
      }
    }

    #endregion // Child

    #region GetDesiredTransform

    /// <summary>
    /// Override.
    /// </summary>
    /// <param name="transform"></param>
    /// <returns></returns>
    public override GeneralTransform GetDesiredTransform( GeneralTransform transform )
    {
      GeneralTransformGroup result = new GeneralTransformGroup();
      result.Children.Add( base.GetDesiredTransform( transform ) );
      result.Children.Add( new TranslateTransform( _offsetLeft, _offsetTop ) );
      return result;
    }

    #endregion // GetDesiredTransform

    #region OffsetLeft

    /// <summary>
    /// Gets/sets the horizontal offset of the adorner.
    /// </summary>
    public double OffsetLeft
    {
      get
      {
        return _offsetLeft;
      }
      set
      {
        _offsetLeft = value;
        UpdateLocation();
      }
    }

    #endregion // OffsetLeft

    #region SetOffsets

    /// <summary>
    /// Updates the location of the adorner in one atomic operation.
    /// </summary>
    public void SetOffsets( double left, double top )
    {
      _offsetLeft = left;
      _offsetTop = top;
      this.UpdateLocation();
    }

    #endregion // SetOffsets

    #region OffsetTop

    /// <summary>
    /// Gets/sets the vertical offset of the adorner.
    /// </summary>
    public double OffsetTop
    {
      get
      {
        return _offsetTop;
      }
      set
      {
        _offsetTop = value;
        UpdateLocation();
      }
    }

    #endregion // OffsetTop

    #endregion // Public Interface

    #region Protected Overrides

    /// <summary>
    /// Override.
    /// </summary>
    /// <param name="constraint"></param>
    /// <returns></returns>
    protected override Size MeasureOverride( Size constraint )
    {
      if( _child == null )
        return base.MeasureOverride( constraint );

      _child.Measure( constraint );
      return _child.DesiredSize;
    }

    /// <summary>
    /// Override.
    /// </summary>
    /// <param name="finalSize"></param>
    /// <returns></returns>
    protected override Size ArrangeOverride( Size finalSize )
    {
      if( _child == null )
        return base.ArrangeOverride( finalSize );

      _child.Arrange( new Rect( finalSize ) );
      return finalSize;
    }

    /// <summary>
    /// Override.
    /// </summary>
    protected override IEnumerator LogicalChildren
    {
      get
      {
        ArrayList list = new ArrayList();
        if( _child != null )
          list.Add( _child );
        return list.GetEnumerator();
      }
    }

    /// <summary>
    /// Override.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    protected override Visual GetVisualChild( int index )
    {
      return _child;
    }

    /// <summary>
    /// Override.
    /// </summary>
    protected override int VisualChildrenCount
    {
      get
      {
        return _child == null ? 0 : 1;
      }
    }

    #endregion // Protected Overrides

    #region Private Helpers

    void UpdateLocation()
    {
      AdornerLayer adornerLayer = base.Parent as AdornerLayer;
      if( adornerLayer != null )
        adornerLayer.Update( base.AdornedElement );
    }

    #endregion // Private Helpers
  }
}
